package node

import (
	"context"
	"sync"
	"sync/atomic"
	"time"

	"github.com/libp2p/go-libp2p-core/host"
	"github.com/libp2p/go-libp2p-core/peer"
	pbp2p "hundsun.com/hsl/hschain/protos/p2p"
)

// 对所连接节点信息进行刷新管理

// PeerInfoManager peer info manager
type PeerInfoManager struct {
	ctx       context.Context
	peerInfo  sync.Map
	host      host.Host
	maxHeight uint64
}

type peerStoreInfo struct {
	storeTime time.Time
	peer      *pbp2p.PeerInfo
}

// NewPeerInfoManager new peer info manager
func NewPeerInfoManager(ctx context.Context, host host.Host) *PeerInfoManager {
	peerInfoManage := &PeerInfoManager{
		ctx:  ctx,
		host: host,
	}
	return peerInfoManage
}

// Refresh refreshes peer info
func (p *PeerInfoManager) Refresh(peer *pbp2p.PeerInfo) {
	if peer == nil {
		return
	}
	storeInfo := peerStoreInfo{
		storeTime: time.Now(),
		peer:      peer,
	}
	p.peerInfo.Store(peer.Name, &storeInfo)
	if peer.GetHeader().GetNumber() > atomic.LoadUint64(&p.maxHeight) {
		atomic.StoreUint64(&p.maxHeight, peer.GetHeader().GetNumber())
	}
}

// Fetch returns info of given peer
func (p *PeerInfoManager) Fetch(pid peer.ID) *pbp2p.PeerInfo {
	key := pid.Pretty()
	v, ok := p.peerInfo.Load(key)
	if !ok {
		return nil
	}
	if info, ok := v.(*peerStoreInfo); ok {
		if time.Since(info.storeTime) > time.Minute {
			p.peerInfo.Delete(key)
			return nil
		}
		return info.peer
	}
	return nil
}

// FetchAll returns all peers info
func (p *PeerInfoManager) FetchAll() []*pbp2p.PeerInfo {
	var peers []*pbp2p.PeerInfo
	var self *pbp2p.PeerInfo
	p.peerInfo.Range(func(key, value interface{}) bool {
		info := value.(*peerStoreInfo)
		if time.Since(info.storeTime) > time.Minute {
			p.peerInfo.Delete(key)
			return true
		}
		if key.(string) == p.host.ID().Pretty() {
			self = info.peer
			return true
		}
		peers = append(peers, info.peer)
		return true
	})
	if self != nil {
		peers = append(peers, self)
	}
	return peers
}

// PeerHeight returns block height of given peer
func (p *PeerInfoManager) PeerHeight(pid peer.ID) uint64 {
	v, ok := p.peerInfo.Load(pid.Pretty())
	if !ok {
		return 0
	}
	info, ok := v.(*peerStoreInfo)
	if !ok {
		return 0
	}
	if info.peer.GetHeader() == nil {
		return 0
	}
	return info.peer.GetHeader().GetNumber()
}

// PeerMaxHeight returns max block height of all connected peers.
func (p *PeerInfoManager) PeerMaxHeight() uint64 {
	return atomic.LoadUint64(&p.maxHeight)
}

//Start Start
func (p *PeerInfoManager) Start() {
	for {
		select {
		case <-p.ctx.Done():
			return
		case <-time.After(time.Second * 30):
			p.prune()
		}
	}
}

func (p *PeerInfoManager) prune() {
	p.peerInfo.Range(func(key interface{}, value interface{}) bool {
		info := value.(*peerStoreInfo)
		if time.Since(info.storeTime) > time.Minute {
			p.peerInfo.Delete(key)
			return true
		}
		return true
	})
}
